/******************************************************************************
* echo_client.c                                                               *
*                                                                             *
* Description: This file contains the C source code for an echo client.  The  *
*              client connects to an arbitrary <host,port> and sends input    *
*              from stdin.                                                    *
*                                                                             *
* Authors: Athula Balachandran <abalacha@cs.cmu.edu>,                         *
*          Wolf Richter <wolf@cs.cmu.edu>                                     *
*                                                                             *
*******************************************************************************/

#define _POSIX_C_SOURCE 200112L  // 使用 POSIX.1-2001 标准
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <signal.h>

#define ECHO_PORT 9999
#define BUF_SIZE 4096
#define SAMPLE_PATH_PREFIX "./samples/"

volatile sig_atomic_t timeout_flag = 0;

// 信号处理函数
void timeout_handler(int signum) {
    timeout_flag = 1;
}

int main(int argc, char* argv[]) {
    char *server_ip = "0.0.0.0";
    char port_str[6];
    snprintf(port_str, sizeof(port_str), "%d", ECHO_PORT);

    if (argc == 3) {
        server_ip = argv[1];
        strncpy(port_str, argv[2], sizeof(port_str) - 1);
        port_str[sizeof(port_str) - 1] = '\0';
    } else if (argc != 1) {
        fprintf(stderr, "usage: %s [<server-ip> <port>]\n", argv[0]);
        return EXIT_FAILURE;
    }

    struct addrinfo hints;
    memset(&hints, 0, sizeof(struct addrinfo));
    struct addrinfo *servinfo;
    hints.ai_family = AF_INET;
    hints.ai_socktype = SOCK_STREAM;
    hints.ai_flags = AI_PASSIVE;

    int status;
    if ((status = getaddrinfo(server_ip, port_str, &hints, &servinfo)) != 0) {
        fprintf(stderr, "getaddrinfo error: %s \n", gai_strerror(status));
        return EXIT_FAILURE;
    }

    int sock;
    if ((sock = socket(servinfo->ai_family, servinfo->ai_socktype, servinfo->ai_protocol)) == -1) {
        fprintf(stderr, "Socket failed");
        return EXIT_FAILURE;
    }

    if (connect(sock, servinfo->ai_addr, servinfo->ai_addrlen) == -1) {
        fprintf(stderr, "Connect failed");
        return EXIT_FAILURE;
    }

    char sample_name[BUF_SIZE];
    char sample_path[BUF_SIZE + sizeof(SAMPLE_PATH_PREFIX)];

    

    while (1) {
        fprintf(stdout, "Please enter the sample file name to send content (enter 'quit' to exit): ");
        if (fgets(sample_name, BUF_SIZE, stdin) == NULL) {
            continue;
        }
        // Remove the newline character
        sample_name[strcspn(sample_name, "\n")] = 0;

        if (strcmp(sample_name, "quit") == 0) {
            break;
        }

        // Construct the full path
        strcpy(sample_path, SAMPLE_PATH_PREFIX);
        strcat(sample_path, sample_name);

        FILE *input_file = fopen(sample_path, "r");
        if (input_file == NULL) {
            perror("Failed to open file");
            continue;
        }

        char msg[BUF_SIZE] = {0};
        size_t total_bytes_read = 0;
        size_t bytes_read;
        // 使用 fread 读取文件内容
        while ((bytes_read = fread(msg + total_bytes_read, 1, BUF_SIZE - total_bytes_read, input_file)) > 0) {
            total_bytes_read += bytes_read;
        }

        fclose(input_file);

        fprintf(stdout, "==========Sending==========\n");
        // 输出发送的内容
        fwrite(msg, 1, total_bytes_read, stdout);

        // 发送文件全部内容
        size_t total_sent = 0;
        while (total_sent < total_bytes_read) {
            ssize_t sent = send(sock, msg + total_sent, total_bytes_read - total_sent, 0);
            if (sent == -1) {
                perror("send failed");
                freeaddrinfo(servinfo);
                close(sock);
                return EXIT_FAILURE;
            }
            total_sent += sent;
        }

        fprintf(stdout, "==========Received==========\n");
        char recv_buf[BUF_SIZE];
        int bytes_received;

        // 设置 3 秒计时器
        signal(SIGALRM, timeout_handler);
        alarm(3);
        timeout_flag = 0;

        while ((bytes_received = recv(sock, recv_buf, BUF_SIZE, 0)) > 0) {
            if (timeout_flag) break;
            recv_buf[bytes_received] = '\0';
            fprintf(stdout, "%s", recv_buf);
            memset(recv_buf, 0, BUF_SIZE);
            // 重置计时器
            alarm(3);
        }

        // 取消计时器
        alarm(0);

        fprintf(stdout, "END\n");

        if(bytes_received == 0) {
            fprintf(stdout, "Connection closed by server\n");
            break;
        }
    }

    freeaddrinfo(servinfo);
    close(sock);
    return EXIT_SUCCESS;
}
